package com.chucang.shucang.usr.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.chucang.shucang.common.base.constant.CommonConstant;
import com.chucang.shucang.common.base.constant.SecurityConstant;
import com.chucang.shucang.common.base.exception.FastException;
import com.chucang.shucang.common.base.page.PageData;
import com.chucang.shucang.common.base.redis.RedisUtil;
import com.chucang.shucang.common.base.utils.DecToSfUtil;
import com.chucang.shucang.common.base.utils.OpsUtil;
import com.chucang.shucang.common.base.utils.R;
import com.chucang.shucang.common.mq.constant.RabbitConstant;
import com.chucang.shucang.common.mq.to.CollectionOrderBaseTO;
import com.chucang.shucang.common.mq.to.NFTDestroyTO;
import com.chucang.shucang.common.mq.to.NFTPublishTO;
import com.chucang.shucang.common.security.annotation.Inner;
import com.chucang.shucang.common.security.entity.ShuCangUser;
import com.chucang.shucang.common.security.utils.SecurityUtil;
import com.chucang.shucang.usr.constant.UsrCacheConstant;
import com.chucang.shucang.usr.constant.UsrCommonConstant;
import com.chucang.shucang.usr.entity.UserBaseEntity;
import com.chucang.shucang.usr.entity.UserCollectionHoldEntity;
import com.chucang.shucang.usr.entity.UserWalletEntity;
import com.chucang.shucang.usr.exception.UsrErrorCode;
import com.chucang.shucang.usr.feign.OmsService;
import com.chucang.shucang.usr.feign.PmsService;
import com.chucang.shucang.usr.feign.vo.CollectionBaseEntity;
import com.chucang.shucang.usr.feign.vo.CollectionMallEntity;
import com.chucang.shucang.usr.feign.vo.CollectionSyntheticEntity;
import com.chucang.shucang.usr.service.UserBaseService;
import com.chucang.shucang.usr.service.UserCollectionHoldService;
import com.chucang.shucang.usr.service.UserWalletService;
import com.chucang.shucang.usr.vo.*;
import com.chucang.shucang.usr.vo.query.UserCollectionHoldQuery;
import com.github.yitter.idgen.YitIdHelper;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户持有藏品信息表
 *
 * @author flitsneak flitsneak@gmail.com
 * @since 1.0.0 2022-09-12
 */
@RestController
@RequestMapping("/usercollectionhold")
@RequiredArgsConstructor
@Api(tags = "用户持有藏品信息表")
public class UserCollectionHoldController {

    private final UserCollectionHoldService userCollectionHoldService;
    private final PmsService pmsService;
    private final OmsService omsService;
    private final UserBaseService userBaseService;
    private final RabbitTemplate rabbitTemplate;
    private final RedissonClient redissonClient;
    private final UserWalletService userWalletService;
    private final RedisUtil redisUtil;

    /**
     * 获取用户拥有藏品信息
     *
     * @param userCollectionHoldQuery ucq
     * @return r
     */
    @PostMapping("/userCollectionPage")
    public R<PageData<UserCollectionHoldResVO>> userCollectionPage(@Valid @RequestBody UserCollectionHoldQuery userCollectionHoldQuery) {
        PageData<UserCollectionHoldResVO> pageData = userCollectionHoldService.userCollectionPage(userCollectionHoldQuery);
        return R.ok(pageData);
    }

    /**
     * 藏品序列号
     *
     * @param userCollectionSerialNumVO ucs
     * @return r
     */
    @PostMapping("/collectionSerialNumList")
    public R<List<UserCollectionSerialNumResVO>> collectionSerialNumList(@Valid @RequestBody UserCollectionSerialNumVO userCollectionSerialNumVO) {
        List<UserCollectionSerialNumResVO> list = userCollectionHoldService.collectionSerialNumList(userCollectionSerialNumVO);
        return R.ok(list);
    }

    /**
     * 藏品寄售
     *
     * @param userCollectionOnConsignVO uco
     * @return r
     */
    @PostMapping("/collectionsOnConsign")
    public R<String> collectionsOnConsign(@Valid @RequestBody UserCollectionOnConsignVO userCollectionOnConsignVO) {
        //是否开通钱包
        LambdaQueryWrapper<UserWalletEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserWalletEntity::getUserId, userCollectionOnConsignVO.getUserId());
        UserWalletEntity one = userWalletService.getOne(queryWrapper);
        if (Objects.isNull(one)) {
            return R.failed(UsrErrorCode.NO_WALLET_FOUND.getCode(), UsrErrorCode.NO_WALLET_FOUND.getMsg());
        }
        if (userCollectionHoldService.collectionsOnConsign(userCollectionOnConsignVO)) {
            return R.ok();
        }
        return R.failed();
    }

    /**
     * 我的藏品详情
     *
     * @param id
     * @param serialNum
     * @return
     */
    @GetMapping("/myCollectionDetail/{id}/{serialNum}")
    public R<MyCollectionDetailResVO> myCollectionDetail(@PathVariable Long id, @PathVariable Integer serialNum) {
        //在售信息
        R<CollectionBaseEntity> collectionBaseInfo = pmsService.getCollectionBaseInfo(id, SecurityConstant.FROM_IN);
        CollectionBaseEntity collectionBaseEntity = OpsUtil.of(collectionBaseInfo)
                .assertDataNotNull(e -> new FastException("查询藏品信息异常！"))
                .getData()
                .get();

        MyCollectionDetailResVO myCollectionDetailResVO = new MyCollectionDetailResVO();
        BeanUtil.copyProperties(collectionBaseEntity, myCollectionDetailResVO);
        //我的藏品信息
        LambdaQueryWrapper<UserCollectionHoldEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper
                .eq(UserCollectionHoldEntity::getCollectionId, id)
                .eq(UserCollectionHoldEntity::getSerialNum, serialNum)
                .eq(UserCollectionHoldEntity::getUserId, SecurityUtil.getUser().getId())
                .eq(UserCollectionHoldEntity::getStatus, UsrCommonConstant.COLLECTION_HOLD)
                .select();
        UserCollectionHoldEntity one = userCollectionHoldService.getOne(lambdaQueryWrapper);
        myCollectionDetailResVO.setSerialNum(serialNum);
        myCollectionDetailResVO.setCollectionSerialNum(String.format(CommonConstant.SERIAL_NUM, serialNum, collectionBaseEntity.getTotalNum()));
        myCollectionDetailResVO.setNftId(one.getNftId());
        //账号信息
        UserBaseEntity baseInfoById = userBaseService.getById(collectionBaseEntity.getPublisherId());
        if (Objects.isNull(baseInfoById)) {
            return R.failed();
        }

        myCollectionDetailResVO.setUsername(baseInfoById.getUsername());
        myCollectionDetailResVO.setOffshoreAccount(baseInfoById.getOffshoreAccount());
        myCollectionDetailResVO.setAccountType(baseInfoById.getAccountType());

        return R.ok(myCollectionDetailResVO);
    }

    /**
     * 商城藏品id
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @GetMapping("/purchaseCollection/{id}")
    public R<PurchaseResVO> purchaseCollection(@PathVariable Long id) {
        ShuCangUser user = SecurityUtil.getUser();
        if (Objects.isNull(user)) {
            return R.failed();
        }
        Long userId = user.getId();
        //是否开通钱包
        LambdaQueryWrapper<UserWalletEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserWalletEntity::getUserId, userId);
        UserWalletEntity one = userWalletService.getOne(queryWrapper);
        if (Objects.isNull(one)) {
            return R.failed(UsrErrorCode.NO_WALLET_FOUND.getCode(), UsrErrorCode.NO_WALLET_FOUND.getMsg());
        }
        //校验用户是否实名认证
        if (user.getIsRealAuth() != UsrCommonConstant.REAL_NAME_AUTH) {
            UserBaseEntity userBaseEntity = userBaseService.getById(userId);
            if (userBaseEntity.getIsRealAuth() != UsrCommonConstant.REAL_NAME_AUTH) {
                return R.failed(UsrErrorCode.NO_REAL_NAME_AUTH.getCode(), UsrErrorCode.NO_REAL_NAME_AUTH.getMsg());
            }
        }
        //锁定库存
        R<CollectionMallEntity> mallEntity = pmsService.lockCollectionStock(id, userId, SecurityConstant.FROM_IN);
        CollectionMallEntity collectionMallEntity = OpsUtil.of(mallEntity)
                .assertCode(CommonConstant.SUCCESS, e -> new FastException("锁定库存失败！"))
                .getData()
                .get();
        //创建订单，发送到订单服务
        long orderId = YitIdHelper.nextId();
        CollectionOrderBaseTO collectionOrderBaseTO = new CollectionOrderBaseTO();
        collectionOrderBaseTO.setId(orderId);
        collectionOrderBaseTO.setCollectionId(collectionMallEntity.getCollectionId());
        collectionOrderBaseTO.setOrderAmount(collectionMallEntity.getSellingPrice());
        long sub = userId % 1000000;
        String orderSn = String.valueOf(orderId) + sub;
        collectionOrderBaseTO.setOrderSn(orderSn);
        collectionOrderBaseTO.setUserId(userId);
        collectionOrderBaseTO.setOrderType(UsrCommonConstant.NORMAL_PURCHASE);
        collectionOrderBaseTO.setOrderNum(1);

        //订单服务同步写入
        R<String> collectionOrder = omsService.createCollectionOrder(collectionOrderBaseTO, SecurityConstant.FROM_IN);
        OpsUtil.of(collectionOrder)
                .assertCode(CommonConstant.SUCCESS, e -> new FastException("写入订单异常！"));
        //放人死信队列
        rabbitTemplate.convertAndSend(RabbitConstant.ORDER_EXCHANGE, RabbitConstant.ORDER_CREATE_ORDER_KEY, collectionOrderBaseTO);
        //藏品详情
        R<CollectionBaseEntity> collectionBaseInfo = pmsService.getCollectionBaseInfo(collectionMallEntity.getCollectionId(), SecurityConstant.FROM_IN);
        CollectionBaseEntity collectionBaseEntity = OpsUtil.of(collectionBaseInfo)
                .assertCode(CommonConstant.SUCCESS, e -> new FastException("获取订单信息异常！"))
                .getData()
                .get();
        PurchaseResVO purchaseResVO = new PurchaseResVO();
        purchaseResVO.setOrderSn(orderSn);
        purchaseResVO.setProductType(UsrCommonConstant.COLLECTION);
        purchaseResVO.setPurchaseNum(1);
        purchaseResVO.setCreateTime(new Date());
        purchaseResVO.setCoverPicture(collectionBaseEntity.getCoverPicture());
        purchaseResVO.setTotalAmount(collectionMallEntity.getSellingPrice());
        purchaseResVO.setSerialNum(collectionMallEntity.getSerialNum());
        purchaseResVO.setProductSerialNum(String.format(UsrCommonConstant.SERIAL_NUM, collectionMallEntity.getSerialNum(), collectionBaseEntity.getTotalNum()));
        return R.ok(purchaseResVO);
    }

    @Inner
    @PostMapping("/getUserCollectionHoldInfos")
    public R<Map<Long, Integer>> getUserCollectionHoldInfos(@RequestBody UserCollectionHoldInfoVO userCollectionHoldInfoVO) {
        LambdaQueryWrapper<UserCollectionHoldEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserCollectionHoldEntity::getUserId, userCollectionHoldInfoVO.getUserId())
                .eq(UserCollectionHoldEntity::getStatus, UsrCommonConstant.COLLECTION_HOLD)
                .in(UserCollectionHoldEntity::getCollectionId, userCollectionHoldInfoVO.getCollectionIdSet());
        List<UserCollectionHoldEntity> list = userCollectionHoldService.list(lambdaQueryWrapper);
        if (CollUtil.isEmpty(list)) {
            return R.failed();
        }
        Map<Long, List<UserCollectionHoldEntity>> listMap = list.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(UserCollectionHoldEntity::getCollectionId));
        Map<Long, Integer> numMap = new HashMap<>(16);
        listMap.forEach((k, v) ->
                numMap.put(k, v.size())
        );
        return R.ok(numMap);
    }

    /**
     * 合成藏品
     *
     * @return r
     */
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/doCollectionSynthetic")
    public R<String> doCollectionSynthetic(@Valid @RequestBody UserCollectionSyntheticVO userCollectionSyntheticVO) throws InterruptedException {
        if (CharSequenceUtil.isBlank(userCollectionSyntheticVO.getSyntheticKey()) || !userCollectionSyntheticVO.getSyntheticKey().equals(DecToSfUtil.toNameSf(userCollectionSyntheticVO.getId()))) {
            return R.failed();
        }
        ShuCangUser user = SecurityUtil.getUser();
        if (Objects.isNull(user)) {
            return R.failed();
        }
        Long userId = user.getId();
        //校验用户是否实名认证
        if (user.getIsRealAuth() != UsrCommonConstant.REAL_NAME_AUTH) {
            UserBaseEntity userBaseEntity = userBaseService.getById(userId);
            if (Objects.isNull(userBaseEntity)) {
                return R.failed();
            }
            if (userBaseEntity.getIsRealAuth() != UsrCommonConstant.REAL_NAME_AUTH) {
                return R.failed(UsrErrorCode.NO_REAL_NAME_AUTH.getCode(), UsrErrorCode.NO_REAL_NAME_AUTH.getMsg());
            }
        }
        //符号合成条件,开始合成
        //开始购买
        RSemaphore semaphore = redissonClient.getSemaphore(String.format(String.format(UsrCacheConstant.COLLECTION_SYNTHETIC, userCollectionSyntheticVO.getSyntheticKey())));
        if (semaphore.tryAcquire(1, 100, TimeUnit.MILLISECONDS)) {
            //获取藏品规则
            R<List<CollectionSyntheticEntity>> syntheticRules = pmsService.getCollectionSyntheticRules(userCollectionSyntheticVO.getId(), userCollectionSyntheticVO.getRule(), SecurityConstant.FROM_IN);
            List<CollectionSyntheticEntity> syntheticEntities = OpsUtil.of(syntheticRules)
                    .assertCode(CommonConstant.SUCCESS, e -> new FastException("获取合成信息失败！"))
                    .getData()
                    .get();
            Map<Long, Integer> idMap = new HashMap<>(16);
            Set<Long> idSet = new HashSet<>(16);
            syntheticEntities.forEach(s -> {
                idMap.put(s.getSyntheticId(), s.getQuantity());
                idSet.add(s.getSyntheticId());
            });
            //逐步扣减 NFT销毁
            LambdaQueryWrapper<UserCollectionHoldEntity> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserCollectionHoldEntity::getUserId, SecurityUtil.getUser().getId())
                    .eq(UserCollectionHoldEntity::getStatus, UsrCommonConstant.COLLECTION_HOLD)
                    .in(UserCollectionHoldEntity::getCollectionId, idSet);
            List<UserCollectionHoldEntity> list = userCollectionHoldService.list(queryWrapper);
            Map<Long, List<UserCollectionHoldEntity>> listMap = list.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(UserCollectionHoldEntity::getCollectionId));
            Map<Long, List<UserCollectionHoldEntity>> cutMap = new HashMap<>(16);
            listMap.forEach((k, v) -> {
                        if (v.size() >= idMap.get(k)) {
                            cutMap.put(k, v.subList(0, idMap.get(k)));
                        } else {
                            throw new FastException("藏品数量不足以合成！");
                        }
                    }
            );
            //本人持有信息更改
            List<Long> ids = new ArrayList<>();
            cutMap.forEach((f, g) ->
                    g.forEach(gg ->
                            ids.add(gg.getId())
                    )
            );
            LambdaUpdateWrapper<UserCollectionHoldEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.in(UserCollectionHoldEntity::getId, ids)
                    .set(UserCollectionHoldEntity::getStatus, 3);
            userCollectionHoldService.update(lambdaUpdateWrapper);
            //发送进行销毁
            //class_id,owner,nft_id,operator_id
            cutMap.forEach((s, m) ->
                    m.forEach(mm -> {
                        if (CharSequenceUtil.isBlank(mm.getNftId())) {
                            return;
                        }
                        NFTDestroyTO nftDestroyTO = new NFTDestroyTO();
                        nftDestroyTO
                                .setNft_id(mm.getNftId())
                                .setOwner(SecurityUtil.getUser().getOffshoreAccount())
                                .setClass_id(CommonConstant.CLASS_ID_PREFIX + s)
                                .setOperation_id(CommonConstant.NFT_DESTROY_PREFIX + mm.getId());
                        rabbitTemplate.convertAndSend(RabbitConstant.NFT_EXCHANGE, RabbitConstant.NFT_DESTROY_NFT_KEY, nftDestroyTO);
                    })
            );
            long incr = redisUtil.incr(String.format(UsrCacheConstant.COLLECTION_SYNTHETIC_STOCK, userCollectionSyntheticVO.getId()), 1);
            NFTPublishTO nftPublishTO = new NFTPublishTO();
            nftPublishTO
                    .setClass_id(CommonConstant.CLASS_ID_PREFIX + userCollectionSyntheticVO.getId())
                    .setName(String.valueOf(incr))
                    .setRecipient(SecurityUtil.getUser().getOffshoreAccount())
                    .setOperation_id(CommonConstant.NFT_PUBLISH_PREFIX + YitIdHelper.nextId())
                    .setUserId(SecurityUtil.getUser().getId())
                    .setSourceType(UsrCommonConstant.FROM_SYNTHETIC);
            rabbitTemplate.convertAndSend(RabbitConstant.NFT_EXCHANGE, RabbitConstant.NFT_PUBLISH_NFT_KEY, nftPublishTO);
            return R.ok();
        }
        return R.failed();
    }

    /**
     * 增加合成藏品
     *
     * @param userCollectionHoldEntity 合成藏品
     * @return r
     */
    @Inner
    @PostMapping("/addCollectionHold")
    public R<String> addCollectionHold(@RequestBody UserCollectionHoldEntity userCollectionHoldEntity) {
        LambdaQueryWrapper<UserCollectionHoldEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserCollectionHoldEntity::getCollectionId, userCollectionHoldEntity.getCollectionId())
                .eq(UserCollectionHoldEntity::getSerialNum, userCollectionHoldEntity.getSerialNum())
                .select();
        UserCollectionHoldEntity one = userCollectionHoldService.getOne(lambdaQueryWrapper);
        if (Objects.isNull(one)) {
            userCollectionHoldService.save(userCollectionHoldEntity);
        }
        return R.ok();
    }

    @Inner
    @PostMapping("/updateCollectionHold")
    public R<String> updateCollectionHold(@RequestBody UserCollectionHoldEntity userCollectionHoldEntity) {
        LambdaUpdateWrapper<UserCollectionHoldEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(UserCollectionHoldEntity::getId, userCollectionHoldEntity.getId())
                .set(UserCollectionHoldEntity::getNftId, userCollectionHoldEntity.getNftId());
        userCollectionHoldService.save(userCollectionHoldEntity);
        return R.ok();
    }
}